# encoding=utf-8
"""
功能:基于redis的ORM模型的编写
作者:wupeaking
创建时间:2016-5-1 15:25:37
"""
import uuid
import redis

REDIS_CONNECT_SET = {
    "host":"localhost",
    "port":6379,
    "db":0,
    "password":None,
}

class ModelManager(object):
    """
    一个模型管理的类 每一个model的类
    默认会拥有一个这样的实例 用于进行
    模型的创建 查找等操作
    """

    def __init__(self, cls):
        self.model_cls = cls
        self._redis =None
    @property
    def redis(self):
        self._redis = self._redis if self._redis else \
            redis.StrictRedis(REDIS_CONNECT_SET["host"], REDIS_CONNECT_SET["port"],
                              REDIS_CONNECT_SET["db"], REDIS_CONNECT_SET["password"])
        return self._redis

    def _get_cur_max_id(self, id=None):
        # 键名为 app.table_name.having.insert.max.id
        key_name = "%s.%s.having.insert.max.id" % (getattr(self.model_cls.Meta, "app_name", RedisModel.Meta.app_name),
                                         getattr(self.model_cls.Meta, "table_name",
                                                 self.model_cls.__name__))
        max_id = self.redis.get(key_name)
        return long(max_id) if max_id else 0

    def _set_cur_max_id(self, id):
        key_name = "%s.%s.having.insert.max.id" % (getattr(self.model_cls.Meta, "app_name", RedisModel.Meta.app_name),
                                 getattr(self.model_cls.Meta, "table_name",
                                         self.model_cls.__name__))
        _id = self.redis.get(key_name)
        max_id = max(_id, id)
        return self.redis.pipeline().set(key_name, long(max_id)).execute()

    def create(self, *args, **kwargs):
        """
        创建一个模型实例 并在redis数据库中插入这条记录
        """
        # 验证所给的参数 是否在模型中定义
        for key in kwargs.keys():
            if not getattr(self.model_cls, key, ""):
                raise AttributeError("model no the attribute")
        # 创建类的实例化对象
        model = self.model_cls()

        # 为该实例化添加特定的属性
        [setattr(model, key, getattr(self.model_cls, key).convert_to_db(kwargs[key]))
         for key in kwargs.keys()]

        # 看是否直接指明ID的值 如果没有指明需要给它redis中还没有的值 否则以这个值为准
        if isinstance(model.id, PrimaryKeyField):
            setattr(model, "id", self._get_cur_max_id()+1)
        # 新的键名
        key_name = "%s.%s.%s" % (self.model_cls.Meta.app_name,
                                         getattr(self.model_cls.Meta, "table_name", self.model_cls.__name__),
                                         model.id)
        cursor = self.redis.pipeline()
        # 对该键的所有字段赋值
        for attr in kwargs.keys():
            cursor.hset(key_name, attr, getattr(model, attr))
        cursor.hset(key_name, "id", model.id)
        cursor.execute()
        self._set_cur_max_id(model.id)
        return model

    def filter(self, *args, **kwargs):
        """
        根据条件进行查找 返回多个模型实例的列表
        """
        pass

    def get(self, *args, **kwargs):
        """
        返回一个实例
        :param args:
        :param kwargs:
        :return:
        """
        pass

    def get_by_id(self, id):
        # 根据id获得键名
        key_name = "%s.%s.%s" % (self.model_cls.Meta.app_name,
                                         getattr(self.model_cls.Meta, "table_name", self.model_cls.__name__),
                                         id)
        result = self.redis.hgetall(key_name)
        if not result:
            return None

        model = self.model_cls()
        # 遍历所有的属性 如果是AbstractField对象 都要进行转换
        for attr in dir(model.__class__):
            attr_ = getattr(model, attr)
            if isinstance(attr_, AbstractField):
                setattr(model, attr, result.get(attr, attr_.default))

        return model


class AbstractField(object):
    def __init__(self, *args, **kwargs):
        self.name = kwargs.get("name", "")
        self.default = kwargs.get("default", None)

class CharField(AbstractField):
    _MAX_LENGTH = 128

    def __init__(self, *args, **kwargs):
        self.max_length = kwargs.get("max_length", CharField._MAX_LENGTH)
        super(CharField, self).__init__(args, kwargs)

    def convert_to_db(self, v):
        value = str(v)
        if len(value) > self.max_length:
            raise ValueError("CharField  max length is setted %s" % self.max_length)
        else:
            return value


class IntField(AbstractField):
    def __init__(self, *args, **kwargs):
        super(IntField, self).__init__(args, kwargs)

    def convert_to_db(self, v):
        value = int(v)
        if value < -2147483648 or value > 2147483647:
            raise ValueError("IntField is 32bit -2147483648 ~ 2147483647")
        else:
            return value


class LongField(AbstractField):
    def convert_to_db(self, v):
        value = long(v)
        if value < -9223372036854775808 or value > 9223372036854775807:
            raise ValueError("longField is 64bit -9223372036854775808 ~ 9223372036854775807")
        else:
            return value


class PrimaryKeyField(LongField):
    def __init__(self, *args, **kwargs):
        self.auto_incr = kwargs.get("auto", True)
        super(PrimaryKeyField, self).__init__(args, kwargs)


class RedisModelBase(type):
    def __init__(cls, name, base, attr):
        super(RedisModelBase, cls).__init__(name, base, attr)
        object = ModelManager(cls)
        setattr(cls, "objects", object)


class RedisModel(object):
    """
    一个redis模型的基类 每次创建的表模型 都应该继承这个类的属性
    """
    __metaclass__ = RedisModelBase
    @classmethod
    def managers(cls):
        return ModelManager(cls)

    # 应该具有的一些私有属性
    class Meta:
        app_name = "redis_orm"
        abstract = False

    # 默认具有一个id
    id = PrimaryKeyField(auto=True)


class TestModel(RedisModel):
    name = CharField(name="aaa", max_length=100)
    age = IntField(default=12)

class Blog(RedisModel):
    title = CharField(max_length=110, default="tittle_default")
    auther = CharField()
    click = IntField()

    class Meta:
        app_name = "wupeaking"

if __name__ == "__main__":
    #REDIS_CONNECT_SET["host"] = "192.168.31.5"
    REDIS_CONNECT_SET.update(host="192.168.31.5")
    #o = TestModel.managers().create(name="id:2", age=11, id=2)
    blog = Blog.objects.create(title="objects_title", click=10, id=200)
    # blog = Blog.managers().get_by_id(blog.id)
    # print blog.id
    # print blog.title
    # print blog.click
    # print blog.auther
    # 修改一把